#include "UtilityPCH.h"
#include "UT_CameraPerspective.h"
#include "UT_MathHelper.h"

using namespace Utility;

//-----------------------------------------------------------------------------
UT_CameraPerspective:: UT_CameraPerspective(const UT_AnimatedTransform &cam2world,
	const float screenWindow[4], float sopen, float sclose,
	float lensr, float focald, float fov, UT_Film *f)
	: UT_CameraProjective(cam2world, UT_MathHelper::MakeTransform_Perspective(fov, 1e-2f, 1000.f),
		screenWindow, sopen, sclose, lensr, focald, f)
{
	// Compute differential changes in origin for perspective camera rays
	m_kCameraXDiff = m_kRasterToCamera(UT_Point3Float(1,0,0)) - m_kRasterToCamera(UT_Point3Float(0,0,0));
	m_kCameraYDiff = m_kRasterToCamera(UT_Point3Float(0,1,0)) - m_kRasterToCamera(UT_Point3Float(0,0,0));
}
//-----------------------------------------------------------------------------
float UT_CameraPerspective::GenerateRay(const UT_CameraSample &sample,UT_Ray *ray) const
{
	// Generate raster and camera samples
	UT_Point3Float Pras(sample.m_fImageX, sample.m_fImageX, 0);
	UT_Point3Float Pcamera;
	m_kRasterToCamera(Pras, &Pcamera);
	
	UT_Vec3Float dir = UT_MathHelper::Normalize(UT_Vec3Float(Pcamera.x, Pcamera.y, Pcamera.z));
	
	*ray = UT_Ray(UT_Point3Float(0,0,0), dir, 0.f, UT_MathDefine::_INFINITY);
	// Modify ray for depth of field
	if (m_fLensRadius > 0.)
	{
		// Sample point on lens
		float lensU, lensV;
		ConcentricSampleDisk(sample.m_fLensU, sample.m_fLensV, &lensU, &lensV);
		lensU *= m_fLensRadius;
		lensV *= m_fLensRadius;

		// Compute point on plane of focus
		float ft				= m_fFocalDistance / ray->d.z;
		UT_Point3Float Pfocus	= (*ray)(ft);

		// Update ray for effect of lens
		ray->o = UT_Point3Float(lensU, lensV, 0.f);
		ray->d = UT_MathHelper::Normalize(Pfocus - ray->o);
	}
	
	ray->time = UT_MathHelper::Lerp(sample.m_fTime, m_fShutterOpenTime, m_fShutterCloseTime);
	m_kCameraToWorld(*ray, ray);
	return 1.f;
}
//-----------------------------------------------------------------------------
float UT_CameraPerspective::GenerateRayDifferential(const UT_CameraSample &sample,UT_RayDifferential *ray) const
{
	// Generate raster and camera samples
	UT_Point3Float Pras(sample.m_fImageX, sample.m_fImageY, 0);
	UT_Point3Float Pcamera;
	m_kRasterToCamera(Pras, &Pcamera);
	
	UT_Vec3Float dir = UT_MathHelper::Normalize(UT_Vec3Float(Pcamera.x, Pcamera.y, Pcamera.z));

	*ray = UT_RayDifferential(UT_Point3Float(0,0,0), dir, 0.f, UT_MathDefine::_INFINITY);

	// Modify ray for depth of field
	if (m_fLensRadius > 0.)
	{
		// Sample point on lens
		float lensU, lensV;
		ConcentricSampleDisk(sample.m_fLensU, sample.m_fLensV, &lensU, &lensV);
		lensU *= m_fLensRadius;
		lensV *= m_fLensRadius;

		// Compute point on plane of focus
		float ft				= m_fFocalDistance / ray->d.z;
		UT_Point3Float Pfocus	= (*ray)(ft);

		// Update ray for effect of lens
		ray->o = UT_Point3Float(lensU, lensV, 0.f);
		ray->d = UT_MathHelper::Normalize(Pfocus - ray->o);
	}

	// Compute offset rays for _PerspectiveCamera_ ray differentials
	ray->rxOrigin = ray->ryOrigin = ray->o;
	ray->rxDirection = UT_MathHelper::Normalize(UT_Vec3Float(Pcamera) + m_kCameraXDiff);
	ray->ryDirection = UT_MathHelper::Normalize(UT_Vec3Float(Pcamera) + m_kCameraXDiff);
	ray->time		 = UT_MathHelper::Lerp(sample.m_fTime, m_fShutterOpenTime, m_fShutterCloseTime);
	
	m_kCameraToWorld(*ray, ray);
	ray->hasDifferentials = true;
	return 1.f;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------